﻿using Xejen.Ipc.Framework.Flows;
using Xejen.Ipc.Framework.Steps;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Xejen.Ipc.Framework.Controllers
{
    /// <summary>
    /// 表示工业控制流的任一处理
    /// </summary>
    /// <typeparam name="TData"><inheritdoc cref="IFlow{TData}" path="/typeparam[@name='TData']"/></typeparam>
    /// <creator>marc</creator>
    internal class Any<TData> : Controller<TData>, IController<TData>
        where TData : new()
    {
        private readonly ControllerInstance<TData> _controllerInstance;
        private readonly Stopwatch _stopwatch = new Stopwatch();

        /// <inheritdoc cref="Any{TData}"/>
        public Any(params IStep<TData>[] steps) : base(steps)
        {
            _controllerInstance = new ControllerInstance<TData>(this);
        }

        /// <inheritdoc/>
        public override async Task RunAsync(IFlowExecutionContext<TData> context, CancellationToken cancellationToken = default)
        {
            OnStarted(this, new ControllerEventArgs<TData>(_controllerInstance));
            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                _controllerInstance.CycleEndTime = null;
                _controllerInstance.CycleExecutionTime = null;

                _controllerInstance.CycleStartTime = StandardTime.Now;
                _stopwatch.Restart();

                var tasks = Steps.Select(async step =>
                {
                    await step.ExecuteAsync(context, cancellationToken);
                });

                await await Task.WhenAny(tasks).ContinueWith(async (t) =>
                {
                    _stopwatch.Stop();
                    _controllerInstance.CycleEndTime = StandardTime.Now;
                    _controllerInstance.CycleExecutionTime = _stopwatch.Elapsed;

                    await base.RunAsync(context, cancellationToken);
                });
            }
            catch (OperationCanceledException exp)
            {
                OnError(this, new ErrorEventArgs<TData>(exp));
            }
            catch (Exception exp)
            {
                OnError(this, new ErrorEventArgs<TData>(exp));
            }
            finally
            {
                OnCompleted(this, new ControllerEventArgs<TData>(_controllerInstance));
            }
        }
    }
}
